Стандартни
преобразования
Ако класовият аргумент не се съпоставя точно, прави се
съпоставяне чрез прилагане на предварително дефинирани стандартни
преобразувания.
Извлечен обект от клас, псевдоним или указател явно се
преобразуват в съответния публичен базов тип клас. Например:
ff(
ZooAnimal& );
ff( Screen& );// ff( ZooAnimal& )ff( yinYang
);*
Указател към произволен тип клас явно се преобразува в указател от
тип void
*.ff( Screen& );
ff( void* );// ff( void* ) ff( yinYang
);
Преобразуване на обект от базов клас, псевдоним или указател в
съответния тип извлечен клас не се прилага. Например, при следващото обръщение
не може да се осъществи съпоставяне:
ff( Bear& );ff( Panda&
);
ZooAnimal za;ff( za ); // error : no match
Присъствието на два или
повече непосредствени базови класове предизвиква маркирането на обръщението като
двузначно. Например, Panda се извлича едновременно от Bear и Endangered. И двете
преобразования на Panda изискват едно и също действие. Тъй като и двете
преобразувания са възможни, обръщението е грешно.
ff( Bear& );ff(
Endangered& );
ff( yinYang ); // error : ambiguous
За да бъде
осъществено обръщението, програмистът трябва явно да укаже в него:
ff(
Bear(yinYang));
Извлеченият клас се разглежда като по-близък до неговия
непосредствен базов клас отколкото до един по-отдалечен базов клас. Следващото
обръщение не е двузначно, макар че и в двата образеца се изисква стандартно
преобразуване. Panda се третира по-скоро като вид Bear, отколкото като вид на
ZooAnimal от алгоритъма за съпоставяне на аргументи.
ff( ZooAnimal&
); ff( Bear& );// ff( Bear& ); ff( yinYang );
Това правило се
разширява да включва и void*. Например, дадена е следната двойка презаредими
функции:
ff( void* );ff( ZooAnimal* );
Аргументът от тип Panda* се
съпоставя с ZooAnimal*.
Дефинирани от потребителя
преобразувания
Дефинираното от потребителя преобразуване може да бъде
конструктор с един аргумент или оператор за явно преобразуване. Дефинираните от
потребителя преобразувания се прилагат само ако не може да се направи точно
съпоставяне или не се открие стандартно преобразуване. За тази част от параграфа
нека осигурим ZooAnimal с две потребителски дефинирани
преобразувания:
class ZooAnimal
{public:
ZooAnimal( long );//
conversion : long ==> ZooAnimal
operator char*();// conversion : ZooAnimal
==> char*
// ...
};
Дадена е следната двойка презаредими
функции:
ff( ZooAnimal& );
ff( Screen& );
Обръщение с
фактически параметър от тип long ще бъде осъществено чрез образеца на ZooAnimal
чрез извличане на потребителски дефинирано преобразувание:
long lval;//
ff( ZooAnimal& )
ff( lval );
Какво ще стане, ако обръщението е с
аргумент int? Например,
ff( 1024 ); // ???
Не може да се направи
нито точно съпоставяне, нито съпоставяне чрез стандартни преобразувания. Има
винаги едно приложимо потребителски дефинирано стандартно преобразуване.
Проблемът е в това, че конструкторът за преобразуване на ZooAnimal очаква
стойност от тип long,а не int. Алгоритъмът за съпоставяне на аргументи ще
приложи стандартно преобразуване за намирането на приложимо потребителски
дефинирано преобразуване. В случая, 1024 се преобразува да бъде от тип long, за
да се възприеме от конструктора на ZooAnimal. Обръщението се осъществява чрез
образеца на ZooAnimal.
Дефинирано от потребителя преобразуване се прилага
само когато друго преобразуване не е възможно. Ако образецът на ff() беше
деклариран да възприема предварително дефиниран тип, операторът за преобразуване
на ZooAnimal нямаше да бъде викан. Например,
ff( ZooAnimal& );
ff(
char );// ff( char );
long lval;
ff( lval );
В този случай е
необходимо явно указване в обръщението за да се осъществи обръщение към образец
на ZooAnimal:
ff( ZooAnimal(lval));// ff( ZooAnimal& )
В
следващия пример се прилага операторът за преобразуване в char* на ZooAnimal тъй
като няма стандартно преобразуване от обект на базов клас в обект на извлечения
клас.
ff( char* );
ff( Bear& );
ZooAnimal za;// za ==> char*
// ff( char* ) ff( za );
Алгоритъмът за съпоставяне на аргументи ще
приложи стандартно преобразуване за постигането на потребителски дефинирано
преобразуване, ако това прави възможно самото съпоставяне. Например,
ff(
Panda* );
ff( void* );
// za ==> char* ==> void
// ff( void*
)
ff( za );
Операторите за преобразуване (но не и конструкторите) се
онаследяват по същия начин като другите членове на класа. И Bear, и Panda
наследяват char* операторът за преобразуване на ZooAnimal. Например,
ff(
char* );
ff( Bear* );
Bear yogi;
Bear pBear = &yogi;// ff( char
)ff( yogi );// ff( pBear ) ff( pBear );
Ако съпоставянето е възможно чрез
прилагането на две или повече потребителски дефинирани преобразувания,
обръщението е двузначно и предизвиква грешка по време на компилация.
Преобразуващите конструктори и операторите за преобразуване имат едно и също
предимство. Ако може да бъде приложен по един образец от всеки тип, обръщението
е двузначно. Например, нека Endangered дефинира оператор за преобразуване от тип
int:
class Endangered {
public:
operator int();// conversion :
Endangered ==> int
// ...};
Тогава ако Extinct дефинира
преобразуващ конструктор, който възприема псевдоним на обект от класа
Endangered, както следва
class Extinct {
public:
Extinct(
Endangered& );
// ...
};
Следващото обръщение е двузначно. И
операторът за преобразуване на Endangered и преобразуващият конструктор на
Extinct могат да осъществят съпоставяне.
ff( Extinct& );
ff( int
);
Endangered e;
ff( e ); // error : ambiguous
Тук има втори пример
на двузначност при извикване на потребителски дефинирано преобразуване. В този
случай преобразуващите конструктори на SmallInt и BitVector са еднакво приложими
- обръщението се маркира като грешно.
class SmallInt
{
public:
SmallInt( int );// conversion : int ==> SmallInt
//
...
};
class BitVector {
BitVector( unsigned long );// conversion :
unsigned long ==> BitVector
// ...};
ff( SmallInt& );
ff(
BitVector& );
ff( 1 ); // error : ambiguous